#include "EffCap.h"
#include "message.h"
#include "DM.h"
#include "parser_helper.h"
#include "utTimingArc.h"
#include <math.h>
#include <map>
#include <cassert>


//----------------------------------------------------------------------------------------------------------//
class EffCap_C::Imp_C
{
    public: //data
        double _dTolerance;
        Net_C* _pNet;
        double _dInputTrans;
        double _dDelayArcFromPin;
        const LibParserLUT& _rTransLUT;
        const LibParserLUT& _rDelayLUT;
        vector<Node_C*> _vNode;
        map<Node_C*,double> _mapDSCap;
        vector<pair<BoundaryNode_C*,float> > _vTransBNode;
        vector<pair<BoundaryNode_C*,float> > _vDelayBNode;
    
    public: //function
        Imp_C(Net_C* p_pNet,double p_dInputTrans,double p_dDelayArcFromPin,const LibParserLUT& p_rTransLUT,const LibParserLUT& p_rDelayLUT,double p_dTolerance);
        void calcDSCap();
        void kernel(); 
        void calcDelay(); 
        void updateEffCap(); 
        void saveResult(); 
        void forward_UpdateTrans();
        void backward_UpdateEffCap();
};
//----------------------------------------------------------------------------------------------------------//
EffCap_C::Imp_C::Imp_C(Net_C* p_pNet,double p_dInputTrans,double p_dDelayArcFromPin,const LibParserLUT& p_rTransLUT,const LibParserLUT& p_rDelayLUT,double p_dTolerance) :
    _dTolerance(p_dTolerance),
    _pNet(p_pNet), _dInputTrans(p_dInputTrans), _dDelayArcFromPin(p_dDelayArcFromPin), _rTransLUT(p_rTransLUT), _rDelayLUT(p_rDelayLUT), 
    _vNode(), _mapDSCap(), _vTransBNode(), _vDelayBNode()
{
    _vNode = p_pNet->getNode();
    unsigned int sz = this->_pNet->getToNode().size() +1;
    _vTransBNode.resize(sz); 
    _vDelayBNode.resize(sz);
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::calcDSCap()
{
    for(int i=this->_vNode.size()-1;i>=0;--i){
        Node_C* pNode = this->_vNode[i];
        const vector<Wire_C*>& vToWire = pNode->getToWire();
        float fDSCap = pNode->getNodeCap();
        for(unsigned int iToWire=0;iToWire<vToWire.size();++iToWire){
            Node_C* pNextNode = vToWire[iToWire]->getToNode();
            fDSCap += this->_mapDSCap.find(pNextNode)->second;
        }
        this->_mapDSCap[pNode] = fDSCap;
        pNode->setEffCap(fDSCap);
    }
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::forward_UpdateTrans()
{
    for(unsigned int iNode=1;iNode<this->_vNode.size();++iNode){
        Node_C* pCurNode = this->_vNode[iNode];
        Node_C* pFromNode = pCurNode->getFromWire()->getFromNode();
        double T = pFromNode->getTransRise(); //Fall?
        double R = pCurNode->getFromWire()->getRes();
        double C = pCurNode->getEffCap();
        double dTransCur = T/(1.0-(R*C/T)*(1.0-exp(-T/(R*C))));
        //double RC_T = R*C/T;
        //double a = (RC_T)*(1.0-exp(-1/RC_T));
        //double b = (1.0-(R*C/T)*(1.0-exp(-T/R*C)));
        //double dTransCur = T/(1.0-a);
        pCurNode->setTransRise(dTransCur);
        //InfoMsg("[forward trans] %s %.3f\n",pCurNode->getName().c_str(),dTransCur);
    }
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::backward_UpdateEffCap()
{
    for(int iNode=this->_vNode.size()-1;iNode>=0;--iNode){
        Node_C* pCurNode = this->_vNode[iNode];
        double T = pCurNode->getTransRise();
        double fEffCap = pCurNode->getNodeCap();
        for(unsigned int iTo=0;iTo<pCurNode->getToWire().size();++iTo){
            Wire_C* pToWire = pCurNode->getToWire()[iTo];
            double R = pToWire->getRes();
            Node_C* pToNode = pToWire->getToNode();
            double C_ds = this->_mapDSCap.find(pToNode)->second;
            double C_eff = pToNode->getEffCap();
            fEffCap += C_ds*(1.0-2.0*(R*C_eff/T)*(1.0-exp(-T/(2.0*R*C_eff))));
        }
        //InfoMsg("[backward ceff] %s %.3f\n",pCurNode->getName().c_str(),fEffCap);
        assert(fEffCap>0);
        pCurNode->setEffCap(fEffCap);
    }
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::kernel()
{
    double dTransFromNode = utTimingArc_C::interpolateLUT(this->_rTransLUT,this->_dInputTrans,this->_mapDSCap.find(_pNet->getFromNode())->second);
    _pNet->getFromNode()->setTransRise(dTransFromNode);
    double dTransFromNode_old = dTransFromNode +this->_dTolerance*10;
    while(1){
        if(this->_dTolerance > fabs(dTransFromNode-dTransFromNode_old)) {break;}
        forward_UpdateTrans();
        backward_UpdateEffCap();
        dTransFromNode_old = dTransFromNode;
        dTransFromNode = utTimingArc_C::interpolateLUT(this->_rTransLUT,this->_dInputTrans,_pNet->getFromNode()->getEffCap());
    }
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::calcDelay()
{
    double dCellDelay(0);
    if(Node_C::ePI==this->_vNode[0]->getType()){
        dCellDelay = 0;
    } else if (Node_C::ePin==this->_pNet->getFromNode()->getType()){
        dCellDelay = utTimingArc_C::interpolateLUT(this->_rDelayLUT,this->_dInputTrans,this->_vNode[0]->getEffCap());
    } else {
        assert(0);
    }
    this->_vNode[0]->setDelayRise(dCellDelay+this->_dDelayArcFromPin);
    for(unsigned int iNode=1;iNode<this->_vNode.size();++iNode){
        Node_C* pCurNode = this->_vNode[iNode];
        float fFromNodeDelay = pCurNode->getFromWire()->getFromNode()->getDelayRise();
        float fRCDelay = pCurNode->getFromWire()->getRes() * pCurNode->getEffCap();
        pCurNode->setDelayRise(fFromNodeDelay+fRCDelay);
    }
}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::Imp_C::saveResult()
{
    _vDelayBNode[0] = make_pair(_pNet->getFromNode(),_pNet->getFromNode()->getDelayRise());
    _vTransBNode[0] = make_pair(_pNet->getFromNode(),_pNet->getFromNode()->getTransRise());
    for(unsigned int i=0;i<_pNet->getToNode().size();++i){
        BoundaryNode_C* pBNode = _pNet->getToNode()[i];
        _vDelayBNode[i+1] = make_pair(pBNode,pBNode->getDelayRise()); 
        _vTransBNode[i+1] = make_pair(pBNode,pBNode->getTransRise()); 
    }
}
//----------------------------------------------------------------------------------------------------------//
EffCap_C::EffCap_C(Net_C* p_pNet,double p_dInputTrans,double p_dDelayArcFromPin,const LibParserLUT& p_rTransLUT,const LibParserLUT& p_rDelayLUT,double p_dTolerance) 
{
    _pImp = new Imp_C(p_pNet,p_dInputTrans,p_dDelayArcFromPin,p_rTransLUT,p_rDelayLUT,p_dTolerance);
}
//----------------------------------------------------------------------------------------------------------//
EffCap_C::~EffCap_C() 
{}
//----------------------------------------------------------------------------------------------------------//
void EffCap_C::run()
{
    _pImp->calcDSCap();
    _pImp->kernel();
    _pImp->calcDelay();
    _pImp->saveResult();
}
//----------------------------------------------------------------------------------------------------------//
const vector<pair<BoundaryNode_C*,float> >& EffCap_C::getDelay()
{
    return this->_pImp->_vDelayBNode;
}
//----------------------------------------------------------------------------------------------------------//
const vector<pair<BoundaryNode_C*,float> >& EffCap_C::getTrans()
{
    return this->_pImp->_vTransBNode;
}
//----------------------------------------------------------------------------------------------------------//



